/*
    SUSAN® - Sole of Unix Save ANything

   
   Copyright (C) 2011-2016 Skyatlas Co. LTD
   

   
*/
/*
 * Kern Sibbald, September MM
 */
/**
 * @file
 * SUSAN Director -- User Agent database handling.
 */

#include "include/susan.h"
#include "dird.h"
#include "dird/jcr_private.h"
#include "dird/ua_db.h"
#include "cats/sql_pooling.h"
#include "dird/ua_select.h"

namespace directordaemon {

/* Imported subroutines */

/**
 * This call explicitly checks for a catalog=catalog-name and
 * if given, opens that catalog.  It also checks for
 * client=client-name and if found, opens the catalog
 * corresponding to that client. If we still don't
 * have a catalog, look for a Job keyword and get the
 * catalog from its client record.
 */
bool OpenClientDb(UaContext* ua, bool use_private)
{
  int i;
  CatalogResource* catalog;
  ClientResource* client;
  JobResource* job;

  /*
   * Try for catalog keyword
   */
  i = FindArgWithValue(ua, NT_("catalog"));
  if (i >= 0) {
    catalog = ua->GetCatalogResWithName(ua->argv[i]);
    if (catalog) {
      if (ua->catalog && ua->catalog != catalog) { CloseDb(ua); }
      ua->catalog = catalog;
      return OpenDb(ua, use_private);
    }
  }

  /*
   * Try for client keyword
   */
  i = FindArgWithValue(ua, NT_("client"));
  if (i >= 0) {
    client = ua->GetClientResWithName(ua->argv[i]);
    if (client) {
      catalog = client->catalog;
      if (ua->catalog && ua->catalog != catalog) { CloseDb(ua); }
      if (!ua->AclAccessOk(Catalog_ACL, catalog->resource_name_, true)) {
        ua->ErrorMsg(_("No authorization for Catalog \"%s\"\n"),
                     catalog->resource_name_);
        return false;
      }
      ua->catalog = catalog;
      return OpenDb(ua, use_private);
    }
  }

  /*
   * Try for Job keyword
   */
  i = FindArgWithValue(ua, NT_("job"));
  if (i >= 0) {
    job = ua->GetJobResWithName(ua->argv[i]);
    if (job && job->client) {
      catalog = job->client->catalog;
      if (ua->catalog && ua->catalog != catalog) { CloseDb(ua); }
      if (!ua->AclAccessOk(Catalog_ACL, catalog->resource_name_, true)) {
        ua->ErrorMsg(_("No authorization for Catalog \"%s\"\n"),
                     catalog->resource_name_);
        return false;
      }
      ua->catalog = catalog;
      return OpenDb(ua, use_private);
    }
  }

  return OpenDb(ua, use_private);
}

/**
 * Open the catalog database.
 */
bool OpenDb(UaContext* ua, bool use_private)
{
  bool mult_db_conn;

  /*
   * See if we need to do any work at all.
   * Point the current used db e.g. ua->db to the correct database connection.
   */
  if (use_private) {
    if (ua->private_db) {
      ua->db = ua->private_db;
      return true;
    }
  } else if (ua->shared_db) {
    ua->db = ua->shared_db;
    return true;
  }

  /*
   * Select the right catalog to use.
   */
  if (!ua->catalog) {
    ua->catalog = get_catalog_resource(ua);
    if (!ua->catalog) {
      ua->ErrorMsg(_("Could not find a Catalog resource\n"));
      return false;
    }
  }

  /*
   * Some modules like bvfs need their own private catalog connection
   */
  mult_db_conn = ua->catalog->mult_db_connections;
  if (use_private) { mult_db_conn = true; }

  ua->jcr->impl->res.catalog = ua->catalog;
  Dmsg0(100, "UA Open database\n");
  ua->db = DbSqlGetPooledConnection(
      ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
      ua->catalog->db_user, ua->catalog->db_password.value,
      ua->catalog->db_address, ua->catalog->db_port, ua->catalog->db_socket,
      mult_db_conn, ua->catalog->disable_batch_insert,
      ua->catalog->try_reconnect, ua->catalog->exit_on_fatal, use_private);
  if (ua->db == NULL) {
    ua->ErrorMsg(_("Could not open catalog database \"%s\".\n"),
                 ua->catalog->db_name);
    return false;
  }
  ua->jcr->db = ua->db;

  /*
   * Save the new database connection under the right label e.g. shared or
   * private.
   */
  if (use_private) {
    ua->private_db = ua->db;
  } else {
    ua->shared_db = ua->db;
  }

  if (!ua->api && !ua->runscript) {
    ua->SendMsg(_("Using Catalog \"%s\"\n"), ua->catalog->resource_name_);
  }

  Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
  return true;
}

void CloseDb(UaContext* ua)
{
  if (ua->jcr) { ua->jcr->db = NULL; }

  if (ua->shared_db) {
    DbSqlClosePooledConnection(ua->jcr, ua->shared_db);
    ua->shared_db = NULL;
  }

  if (ua->private_db) {
    DbSqlClosePooledConnection(ua->jcr, ua->private_db);
    ua->private_db = NULL;
  }
}

/**
 * Create a pool record from a given Pool resource
 *
 * Returns: -1  on error
 *           0  record already exists
 *           1  record created
 */
int CreatePool(JobControlRecord* jcr,
               SusanDb* db,
               PoolResource* pool,
               e_pool_op op)
{
  PoolDbRecord pr;

  bstrncpy(pr.Name, pool->resource_name_, sizeof(pr.Name));

  if (db->GetPoolRecord(jcr, &pr)) {
    /*
     * Pool Exists
     */
    if (op == POOL_OP_UPDATE) { /* update request */
      SetPooldbrFromPoolres(&pr, pool, op);
      SetPooldbrReferences(jcr, db, &pr, pool);
      db->UpdatePoolRecord(jcr, &pr);
    }
    return 0; /* exists */
  }

  SetPooldbrFromPoolres(&pr, pool, op);
  SetPooldbrReferences(jcr, db, &pr, pool);

  if (!db->CreatePoolRecord(jcr, &pr)) { return -1; /* error */ }
  return 1;
}

/**
 * This is a common routine used to stuff the Pool DB record defaults
 * into the Media DB record just before creating a media (Volume) record.
 */
void SetPoolDbrDefaultsInMediaDbr(MediaDbRecord* mr, PoolDbRecord* pr)
{
  mr->PoolId = pr->PoolId;
  bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
  mr->Recycle = pr->Recycle;
  mr->VolRetention = pr->VolRetention;
  mr->VolUseDuration = pr->VolUseDuration;
  mr->ActionOnPurge = pr->ActionOnPurge;
  mr->RecyclePoolId = pr->RecyclePoolId;
  mr->MaxVolJobs = pr->MaxVolJobs;
  mr->MaxVolFiles = pr->MaxVolFiles;
  mr->MaxVolBytes = pr->MaxVolBytes;
  mr->LabelType = pr->LabelType;
  mr->MinBlocksize = pr->MinBlocksize;
  mr->MaxBlocksize = pr->MaxBlocksize;
  mr->Enabled = VOL_ENABLED;
}

/**
 * Set PoolDbRecord.RecyclePoolId and PoolDbRecord.ScratchPoolId from Pool
 * resource works with set_pooldbr_from_poolres
 */
bool SetPooldbrReferences(JobControlRecord* jcr,
                          SusanDb* db,
                          PoolDbRecord* pr,
                          PoolResource* pool)
{
  bool ret = true;

  if (pool->RecyclePool) {
    PoolDbRecord rpool;

    bstrncpy(rpool.Name, pool->RecyclePool->resource_name_, sizeof(rpool.Name));
    if (db->GetPoolRecord(jcr, &rpool)) {
      pr->RecyclePoolId = rpool.PoolId;
    } else {
      Jmsg(jcr, M_WARNING, 0,
           _("Can't set %s RecyclePool to %s, %s is not in database.\n"
             "Try to update it with 'update pool=%s'\n"),
           pool->resource_name_, rpool.Name, rpool.Name, pool->resource_name_);

      ret = false;
    }
  } else { /* no RecyclePool used, set it to 0 */
    pr->RecyclePoolId = 0;
  }

  if (pool->ScratchPool) {
    PoolDbRecord rpool;

    bstrncpy(rpool.Name, pool->ScratchPool->resource_name_, sizeof(rpool.Name));
    if (db->GetPoolRecord(jcr, &rpool)) {
      pr->ScratchPoolId = rpool.PoolId;
    } else {
      Jmsg(jcr, M_WARNING, 0,
           _("Can't set %s ScratchPool to %s, %s is not in database.\n"
             "Try to update it with 'update pool=%s'\n"),
           pool->resource_name_, rpool.Name, rpool.Name, pool->resource_name_);
      ret = false;
    }
  } else { /* no ScratchPool used, set it to 0 */
    pr->ScratchPoolId = 0;
  }

  return ret;
}

/**
 * This is a common routine to create or update a
 * Pool DB base record from a Pool Resource. We handle
 * the setting of MaxVols and NumVols slightly differently
 * depending on if we are creating the Pool or we are
 * simply bringing it into agreement with the resource (update).
 *
 * Caution : RecyclePoolId isn't setup in this function.
 *           You can use set_pooldbr_recyclepoolid();
 */
void SetPooldbrFromPoolres(PoolDbRecord* pr, PoolResource* pool, e_pool_op op)
{
  bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
  if (op == POOL_OP_CREATE) {
    pr->MaxVols = pool->max_volumes;
    pr->NumVols = 0;
  } else {
    /*
     * Update pool
     */
    if (pr->MaxVols != pool->max_volumes) { pr->MaxVols = pool->max_volumes; }
    if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
      pr->MaxVols = pr->NumVols;
    }
  }
  pr->LabelType = pool->LabelType;
  pr->UseOnce = pool->use_volume_once;
  pr->UseCatalog = pool->use_catalog;
  pr->Recycle = pool->Recycle;
  pr->VolRetention = pool->VolRetention;
  pr->VolUseDuration = pool->VolUseDuration;
  pr->MaxVolJobs = pool->MaxVolJobs;
  pr->MaxVolFiles = pool->MaxVolFiles;
  pr->MaxVolBytes = pool->MaxVolBytes;
  pr->AutoPrune = pool->AutoPrune;
  pr->ActionOnPurge = pool->action_on_purge;
  pr->ActionOnPurge = pool->action_on_purge;
  pr->MinBlocksize = pool->MinBlocksize;
  pr->MaxBlocksize = pool->MaxBlocksize;
  pr->Recycle = pool->Recycle;
  if (pool->label_format) {
    bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
  } else {
    bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
  }
}

/**
 * set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog
 */
int UpdatePoolReferences(JobControlRecord* jcr,
                         SusanDb* db,
                         PoolResource* pool)
{
  PoolDbRecord pr;

  if (!pool->RecyclePool && !pool->ScratchPool) { return true; }
  bstrncpy(pr.Name, pool->resource_name_, sizeof(pr.Name));

  if (!db->GetPoolRecord(jcr, &pr)) { return -1; /* not exists in database */ }

  SetPooldbrFromPoolres(&pr, pool, POOL_OP_UPDATE);

  if (!SetPooldbrReferences(jcr, db, &pr, pool)) { return -1; /* error */ }

  if (!db->UpdatePoolRecord(jcr, &pr)) { return -1; /* error */ }
  return true;
}
} /* namespace directordaemon */
